Skip to content

Agent skills, an llms.txt API index, and the defects verifying them uncovered - #1620

Merged
obiot merged 8 commits into
masterfrom
feat/agent-skills
Aug 31, 2026
Merged

Agent skills, an llms.txt API index, and the defects verifying them uncovered#1620
obiot merged 8 commits into
masterfrom
feat/agent-skills

Conversation

@obiot

@obiot obiot commented Aug 30, 2026

Copy link
Copy Markdown
Member

Ships melonJS's guidance for AI coding assistants, and fixes the six code defects that verifying it against the source turned up.

Skills

23 task-oriented guides at packages/melonjs/skills/, versioned with the engine so the guidance always matches the release in node_modules. Each covers one subsystem and ends with a symptom → cause table, because melonJS's characteristic failure is silent rather than fatal — the engine warns once and degrades, so what the user sees is wrong output, not a stack trace.

  • Claude Code: /plugin marketplace add melonjs/melonJS (manifests in .claude-plugin/, claude plugin validate --strict passes)
  • Codex / Cursor / Gemini CLI: skills/AGENTS.md ships ready to copy into a game's project root
  • melonjs/SKILL.md is a router — an agent reads it, then the one or two guides it points at, not all 23

llms.txt

scripts/generate-llms-txt.ts runs after typedoc in pnpm doc, so the docs workflow publishes an index of every exported symbol at https://melonjs.github.io/melonJS/llms.txt. It is the escape hatch for anything the skills do not cover.

It reads the same TSDoc the reference pages render, so it cannot drift from them. Two details that took iteration: deprecated entries are marked, since those are precisely what a model trained on older melonJS reaches for; and the 50 String.prototype pages typedoc emits for the loader.nocache string are skipped, because indexing them would tell an agent that loader.nocache.trimEnd is part of the melonJS API. (Those pages are still in the published reference — a separate issue.)

Six defects, each proven by a failing test first

defect proof before the fix
Tiled objects with no geometry got a triangular collision shape bodyDef.shapes[0].contains(4, 28)false for a 32×32 rect
timer.setInterval discarded its pauseable argument stored true for false; never fired while paused — setTimeout passed the same test
loader error path read this.onError from a module function detached preload rejected with Cannot read properties of undefined (reading 'onError')
loader.onload / onProgress / onError unsettable assignment throws TypeError — the documented callback API was unusable
input.preventDefault unsettable same, and no setter existed
Canvas setBlendMode("none") resolved by fall-through now explicit, with the reason stated where the switch is read

The Tiled one is the most consequential: (0,0), (w,0), (w,h) omits the fourth vertex, so the lower-left half of every plain rectangular object in every map was not solid. Walk into it from the left and you pass through.

Two are removals rather than repairs. loader.onload / onProgress / onError were let bindings on an ES module namespace — assigning them always threw, so the migration path documented since 18.2.0 never worked for anyone, and loader.js's onProgress branch was dead code. input.preventDefault went the other way and gained setPreventDefault(), since it is a live option the engine reads rather than a deprecated one.

API reference corrected against the code

The same verification pass found the docs disagreeing with the source in 30 places. The rule applied throughout: where JSDoc and code disagree, the code wins and the doc changes — except timer.ts, where the code was the defect.

  • Renderable#draw told you to draw at (0, 0); preDraw never translates to this.pos
  • the body @example called viewport.follow(this.pos, …), which throws
  • Camera3d documented a roll axis that does not exist
  • Mesh omitted eight constructor settings, including lit — the switch for the whole lit-mesh path
  • the Asset typedef advertised "tmj" / "tsj" types that throw (extensions, not types)
  • Body#addShape's documentation had drifted onto a private helper, leaving the public method undocumented
  • 18 effect classes carried an unresolvable @param import path, and 33 examples across 20 files taught renderable.shader =, deprecated since 19.2.0

The wiki carried three of the same errors and is already fixed (a24ab3e, 6e168f7) — two of them were where the engine's own JSDoc got them from.

Housekeeping

  • melonjs20.3.0, so the built version export, console header and plugin.register gate match the unreleased line
  • release.ts now aborts before tagging if .claude-plugin/plugin.json has drifted from it
  • root tsconfig.json gains types: ["node"]scripts/ had 8 pre-existing type errors because node built-ins were unresolved, invisible to CI since turbo test:types only runs inside packages

Verification

6431 tests passing (264 files, 0 failures) · eslint 0 errors · biome clean · tsc clean · typedoc 0 errors · claude plugin validate --strict passes · npm pack ships all 23 skills plus AGENTS.md

Closes #1619

🤖 Generated with Claude Code

https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N

…ncovered

Ship 23 task-oriented skills at `packages/melonjs/skills/`, versioned with the
engine and installable in Claude Code as a plugin. Each covers one subsystem
and ends with a symptom-to-cause table, because melonJS's characteristic
failure is silent rather than fatal: the engine warns once and degrades, so
wrong output — not a stack trace — is what the user sees.

`AGENTS.md` ships alongside them for agents following that convention (Codex,
Cursor, Gemini CLI), to copy into a game's project root.

Add `scripts/generate-llms-txt.ts`, chained onto `pnpm doc`, publishing an
index of every exported symbol at melonjs.github.io/melonJS/llms.txt — the
escape hatch for anything the skills do not cover. It reads the same TSDoc the
reference pages render, so it cannot drift; marks deprecated entries, which are
exactly what a model trained on older melonJS reaches for; and skips the 50
`String.prototype` pages typedoc emits for the `loader.nocache` string, which
would otherwise present `loader.nocache.trimEnd` as melonJS API.

Verifying the skills against the source found six code defects. Each is fixed
with a test written to fail first:

- Tiled: an object with no explicit geometry got a *triangular* collision
  shape — `(0,0), (w,0), (w,h)` omits the fourth vertex, so the lower-left half
  of every plain rectangle in a map was not solid.
- Timer: `setInterval`'s `pauseable` argument was discarded by
  `pauseable === true || true`, so no repeating timer could survive a pause.
  `setTimeout` was unaffected (#1619).
- Loader: the error path read `this.onError` from a module function, which has
  no `this` — a detached `preload` reported "Cannot read properties of
  undefined" instead of the real load failure.
- Loader: `onload` / `onProgress` / `onError` are removed. Deprecated since
  18.2.0, they were `let` bindings on a module namespace, so assigning them
  always threw; the documented migration path never worked, and the branch
  guarding `onProgress` was dead code.
- Input: `preventDefault` is documented as a global option but is read-only for
  the same reason. `setPreventDefault()` is the working form.
- Canvas: `setBlendMode("none")` resolves to `"normal"` explicitly rather than
  by fall-through — there is no `globalCompositeOperation` that disables
  blending for the drawn area alone.

The same pass corrected the API reference against the code it documents:
`Renderable#draw` told you to draw at `(0, 0)` when `preDraw` never translates
to `this.pos`; the `body` example called `viewport.follow(this.pos, …)`, which
throws; `Camera3d` documented a `roll` axis that does not exist; `Mesh` omitted
eight constructor settings including `lit`; the `Asset` typedef advertised
`"tmj"` / `"tsj"` types that throw; `Body#addShape`'s documentation had drifted
onto a private helper; and 18 effect classes carried an unresolvable `@param`
import path plus examples teaching `renderable.shader =`, deprecated since
19.2.0.

Also: bump melonjs to 20.3.0 so the built `version` matches the unreleased
line, guard the plugin manifest against drifting from it in `release.ts`, and
give the root tsconfig `types: ["node"]` so `scripts/` type-checks at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI lite review requested due to automatic review settings August 30, 2026 09:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread packages/melonjs/scripts/generate-llms-txt.ts Fixed
Two fixes to the marketplace-facing blurb.

The em dash was escaped as — — a Python json.dump artifact, not a choice.
It parses identically, but nothing else in the repo is written that way.

More importantly the wording led with "the pitfalls that make generated code
fail silently". That is the honest reason the skills exist and it belongs in
them, but as the first line someone reads it describes the engine as a
minefield rather than saying what installing this gets you.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 30, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Its own JSDoc shows a rainbow-gradient example, so it is the obvious thing to
reach for when a prompt asks for a trail — and under a Camera3d it draws a flat
ribbon that does not recede with perspective. Names the two routes that do work
in 3D: a world-space emitter, or a ribbon mesh with vertexColors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 30, 2026 23:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

"2.5D engine" undersells both ends: someone looking for a 2D platformer may not
realise that is the engine's core, and nobody would guess there is a real 3D
tier — perspective cameras, meshes, glTF scenes, instancing, 3D lights.

It also matters for skill selection. The router's description is what gets
matched against a task, so a prompt phrased purely in 2D or purely in 3D terms
should hit it.

Applied to both plugin manifests, the router skill and the shipped AGENTS.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 31, 2026 00:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Reviewed down to what a user acts on. The core-js removal and the two loader
restructurings (moving the fetch helper to utils/, registering audio through a
parser) are internal — no API changed, so they belong in the commit history
rather than in release notes someone reads to decide whether to upgrade.
`setPreventDefault` moved to Added, where a new export belongs.

Two corrections on top of that review: the Howler sentence was missing an
article, and it still said the vendoring removed "the last runtime dependency
besides core-js". core-js is gone as of this same release and no longer
mentioned anywhere in the file, so that clause left the reader thinking it
remains. melonjs now declares no runtime dependencies at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 31, 2026 00:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The README and the website already state that the engine has no dependencies,
so restating it as news is redundant — and it made the entry lead with a
property rather than with the change. What a reader needs here is that the
audio backend is in-tree now, and that the type declarations no longer point at
a package they had to install themselves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 31, 2026 00:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 31, 2026 00:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

CodeQL flagged `replace(/<[^>]+>/g, "")` as an incomplete sanitizer, and it is
right about the shape: an unterminated `<script` never matches the pattern and
survives into the output. Harmless here — the input is typedoc's own HTML and
the output is a text file, never a DOM — but it is a new high-severity alert on
a release, and the sound version is ten lines.

Scanning from `<` to the next `>` cannot leave a tag behind, and an
unterminated one takes the rest of the fragment with it. Verified against both
shapes CodeQL cites, and the generated llms.txt is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N
Copilot AI review requested due to automatic review settings August 31, 2026 00:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@obiot
obiot merged commit 8e2f9be into master Aug 31, 2026
6 checks passed
@obiot
obiot deleted the feat/agent-skills branch August 31, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

timer.setInterval ignores its pauseable argument

3 participants